// 版权所有2016 etcd作者
// 
// 根据Apache许可证2.0版（以下简称“许可证”）获得许可；
// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

package recipe

import (
	"context"

	"go.etcd.io/etcd/api/v3/mvccpb"
	v3 "go.etcd.io/etcd/client/v3"
)

// 屏障在etcd中创建一个密钥来阻止进程，然后删除该密钥
// 释放所有被阻止的进程。
type Barrier struct {
	client *v3.Client
	ctx    context.Context

	key string
}

func NewBarrier(client *v3.Client, key string) *Barrier {
	return &Barrier{client, context.TODO(), key}
}

// Hold创建屏障密钥，导致进程在等待时阻塞。
func (b *Barrier) Hold() error {
	_, err := newKey(b.client, b.key, v3.NoLease)
	return err
}

// Release删除屏障密钥以解除所有等待进程的阻塞。
func (b *Barrier) Release() error {
	_, err := b.client.Delete(b.ctx, b.key)
	return err
}

// 等待屏障键上的块，直到它被删除。如果没有密钥，请等待
// 假定已调用Release并立即返回。
func (b *Barrier) Wait() error {
	resp, err := b.client.Get(b.ctx, b.key, v3.WithFirstKey()...)
	if err != nil {
		return err
	}
	if len(resp.Kvs) == 0 {
		// 密钥已删除
		return nil
	}
	_, err = WaitEvents(
		b.client,
		b.key,
		resp.Header.Revision,
		[]mvccpb.Event_EventType{mvccpb.PUT, mvccpb.DELETE})
	return err
}
